/**
 * (C) Copyright IBM Corp. 2010, 2015
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 */

package com.ibm.bi.dml.hops;

import com.ibm.bi.dml.lops.Data;
import com.ibm.bi.dml.lops.Lop;
import com.ibm.bi.dml.lops.LopProperties.ExecType;
import com.ibm.bi.dml.lops.LopsException;
import com.ibm.bi.dml.parser.Expression.DataType;
import com.ibm.bi.dml.parser.Expression.ValueType;
import com.ibm.bi.dml.runtime.util.UtilFunctions;


public class LiteralOp extends Hop 
{
	
	private double value_double = Double.NaN;
	private long value_long = Long.MAX_VALUE;
	private String value_string;
	private boolean value_boolean;

	// INT, DOUBLE, STRING, BOOLEAN

	private LiteralOp() {
		//default constructor for clone
	}
	
	public LiteralOp(double value) {
		super(String.valueOf(value), DataType.SCALAR, ValueType.DOUBLE);
		this.value_double = value;
	}

	public LiteralOp(long value) {
		super(String.valueOf(value), DataType.SCALAR, ValueType.INT);
		this.value_long = value;
	}

	public LiteralOp(String value) {
		super(value, DataType.SCALAR, ValueType.STRING);
		this.value_string = value;
	}

	public LiteralOp(boolean value) {
		super(String.valueOf(value), DataType.SCALAR, ValueType.BOOLEAN);
		this.value_boolean = value;
	}

	
	@Override
	public Lop constructLops()
		throws HopsException, LopsException  
	{	
		//return already created lops
		if( getLops() != null )
			return getLops();

		
		try 
		{
			Lop l = null;

			switch (getValueType()) {
			case DOUBLE:
				l = Data.createLiteralLop(ValueType.DOUBLE, Double.toString(value_double));
				break;
			case BOOLEAN:
				l = Data.createLiteralLop(ValueType.BOOLEAN, Boolean.toString(value_boolean));
				break;
			case STRING:
				l = Data.createLiteralLop(ValueType.STRING, value_string);
				break;
			case INT:
				l = Data.createLiteralLop(ValueType.INT, Long.toString(value_long));
				break;
			default:
				throw new HopsException(this.printErrorLocation() + 
						"unexpected value type constructing lops for LiteralOp.\n");
			}

			l.getOutputParameters().setDimensions(0, 0, 0, 0, -1);
			setLineNumbers(l);
			setLops(l);
		} 
		catch(LopsException e) {
			throw new HopsException(e);
		}
		
		//note: no reblock lop because always scalar
		
		return getLops();
	}

	public void printMe() throws HopsException {
		if (LOG.isDebugEnabled()){
			if (getVisited() != VisitStatus.DONE) {
				super.printMe();
				switch (getValueType()) {
				case DOUBLE:
					LOG.debug("  Value: " + value_double);
					break;
				case BOOLEAN:
					LOG.debug("  Value: " + value_boolean);
					break;
				case STRING:
					LOG.debug("  Value: " + value_string);
					break;
				case INT:
					LOG.debug("  Value: " + value_long);
					break;
				default:
					throw new HopsException(this.printErrorLocation() +
							"unexpected value type printing LiteralOp.\n");
				}

				for (Hop h : getInput()) {
					h.printMe();
				}

			}
			setVisited(VisitStatus.DONE);
		}
	}

	@Override
	public String getOpString() {
		String val = null;
		switch (getValueType()) {
			case DOUBLE:
				val = Double.toString(value_double);
				break;
			case BOOLEAN:
				val = Boolean.toString(value_boolean);
				break;
			case STRING:
				val = value_string;
				break;
			case INT:
				val = Long.toString(value_long);
				break;
			default:
				val = "";
		}
		return "LiteralOp " + val;
	}
		
	@Override
	protected double computeOutputMemEstimate( long dim1, long dim2, long nnz )
	{		
		double ret = 0;
		
		switch( getValueType() ) {
			case INT:
				ret = OptimizerUtils.INT_SIZE; break;
			case DOUBLE:
				ret = OptimizerUtils.DOUBLE_SIZE; break;
			case BOOLEAN:
				ret = OptimizerUtils.BOOLEAN_SIZE; break;
			case STRING: 
				ret = this.value_string.length() * OptimizerUtils.CHAR_SIZE; break;
			case OBJECT:
				ret = OptimizerUtils.DEFAULT_SIZE; break;
			default:
				ret = 0;
		}
		
		return ret;
	}
	
	@Override
	protected double computeIntermediateMemEstimate( long dim1, long dim2, long nnz )
	{
		return 0;
	}
	
	@Override
	protected long[] inferOutputCharacteristics( MemoTable memo )
	{
		return null;
	}
	
	@Override
	public boolean allowsAllExecTypes()
	{
		return false;
	}	
	
	@Override
	protected ExecType optFindExecType() throws HopsException {
		// Since a Literal hop does not represent any computation, 
		// this function is not applicable. 
		return null;
	}
	
	@Override
	public void refreshSizeInformation()
	{
		//do nothing; it is a scalar
	}
	
	public long getLongValue() throws HopsException 
	{
		switch( getValueType() ) {
			case INT:		
				return value_long;
			case DOUBLE:	
				return UtilFunctions.toLong(value_double);
			case STRING:
				return Long.parseLong(value_string);	
			default:
				throw new HopsException("Can not coerce an object of type " + getValueType() + " into Long.");
		}
	}
	
	public double getDoubleValue() throws HopsException {
		switch( getValueType() ) {
			case INT:		
				return value_long;
			case DOUBLE:	
				return value_double;
			case STRING:
				return Double.parseDouble(value_string);
			default:
				throw new HopsException("Can not coerce an object of type " + getValueType() + " into Double.");
		}
	}
	
	public boolean getBooleanValue() throws HopsException {
		if ( getValueType() == ValueType.BOOLEAN ) {
			return value_boolean;
		}
		else
			throw new HopsException("Can not coerce an object of type " + getValueType() + " into Boolean.");
	}
	
	public String getStringValue() 
	{
		switch( getValueType() ) {
			case BOOLEAN:
				return String.valueOf(value_boolean);
			case INT:		
				return String.valueOf(value_long);
			case DOUBLE:	
				return String.valueOf(value_double);
			case STRING:
				return value_string;
			case OBJECT:
			case UNKNOWN:	
				//do nothing (return null)
		}
		
		return null;
	}
	
	@Override
	public Object clone() throws CloneNotSupportedException 
	{
		LiteralOp ret = new LiteralOp();	
		
		//copy generic attributes
		ret.clone(this, false);
		
		//copy specific attributes
		ret.value_double = value_double;
		ret.value_long = value_long;
		ret.value_string = value_string;
		ret.value_boolean = value_boolean;
		
		return ret;
	}
	
	@Override
	public boolean compare( Hop that )
	{
		return false;
	}
}
